/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.cef.command;

import cef.command.Command;
import cef.command.CommandException;
import genomemap.cef.event.ProcessComplDataEvent;
import genomemap.data.OrganismData;

import java.util.*;

/**
 * @author Susanta Tewari
 * @version 1.0 Nov 21, 2010
 */
public class ComplData_Cmd extends Command<ProcessComplDataEvent> {

    /**
     * assignments by linkage groups
     */
    private Map<Integer, Map<String, Set<String>>> assignments;

    /**
     * input-assignment-gene which is found in the organism data but the corresponding
     * input-assignment-clones were not found.
     */
    private Set<String> genes_with_empty_clones;

    /**
     * input-assignment-gene not found in the organism data
     */
    private Set<String> genes_not_found;

    public ComplData_Cmd(final ProcessComplDataEvent event) {
        super(event);
    }

    /**
     * Method description
     *
     *
     * @throws CommandException
     */
    @Override
    public void executeImpl() throws CommandException {


        // 2.  get the input assignments
        final Map<String, List<String>> input_assignments =
            getEvent().getAssignmentData().getGeneAssignments();


        // 3. get an instance of the associated organism data
        final OrganismData inputData = getEvent().getOrganismData();


        // 4. create the assignment data. for every input assignment check if the
        // input-assignment-gene is present in a linkage group of the associated organism data, if so,
        // keep only the input-assignment-clones that arepresent in the organism data for that
        // linkage group. Record input-assignment-genes not found in the organism data and also the
        // ones which are found but not their corresponding input-assignment-clones
        // 4.1 init map
        assignments = new HashMap<>();

        for (final Integer linkageGroup : inputData.getLinkageGroups()) {
            assignments.put(linkageGroup, new HashMap<String, Set<String>>());
        }


        // 4.2 init. genes_with_empty_clones and genes_not_found
        genes_with_empty_clones = new HashSet<>();
        genes_not_found         = new HashSet<>();


        // 4.3 process each input-assignment-gene
        for (final String input_assignment_gene : input_assignments.keySet()) {


            // 4.3.1 find the linkage group in organism data to which the input-assignment-gene lies
            boolean found = false;

            for (final Integer linkageGroup : inputData.getLinkageGroups()) {

                final List<String> organism_data_genes = inputData.genes(linkageGroup);

                if (organism_data_genes.contains(input_assignment_gene)) {


                    // 4.3.1.1 found the linkage group
                    found = true;


                    // 4.3.1.2 find the organism data clones
                    final Set<String> organism_data_clones = inputData.clones(linkageGroup);


                    // 4.3.1.3 get the input-assignment-clones
                    final Set<String> input_assignment_clones =
                        new HashSet<>(input_assignments.get(input_assignment_gene));


                    // 4.3.1.4 remove the assignment clones not present in the organism data
                    input_assignment_clones.retainAll(organism_data_clones);


                    // 4.3.1.5 check if the assignment clones is empty
                    if (!input_assignment_clones.isEmpty()) {


                        // 4.3.1.5.1 No, it is not empty. Fill assignment data
                        assignments.get(linkageGroup).put(input_assignment_gene,
                                        input_assignment_clones);
                    } else {


                        // 4.3.1.5.2 Yes, it is empty. Record in genes_with_empty_clones
                        genes_with_empty_clones.add(input_assignment_gene);
                    }

                    break;
                }
            }


            // 4.3.2 no linkage group found. Record in genes_not_found
            if (!found) {
                genes_not_found.add(input_assignment_gene);
            }
        }
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public Map<Integer, Map<String, Set<String>>> getAssignments() {

        checkCmdFinished();

        return assignments;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public Integer getTotalAssignmentCount() {

        checkCmdFinished();

        final Map<Integer, Map<String, Set<String>>> map = getAssignments();
        int total                                        = 0;

        for (final Integer lg : map.keySet()) {
            total += map.get(lg).size();
        }

        return total;
    }

    /**
     * @return assignment counts by the linkage groups
     */
    public Map<Integer, Integer> getAssignmentCountsByLG() {

        checkCmdFinished();

        final Map<Integer, Map<String, Set<String>>> map = getAssignments();
        final Map<Integer, Integer> result               = new HashMap<>();

        for (final Integer lg : map.keySet()) {
            result.put(lg, map.get(lg).size());
        }

        return result;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public Set<String> getGenesNotFound() {

        checkCmdFinished();

        return genes_not_found;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public Set<String> getGenesWithEmptyClones() {

        checkCmdFinished();

        return genes_with_empty_clones;
    }
}
